#include "astree.h"

node* createNode(unsigned int type, row *value, node *s1, node *s2, node *s3, node *s4, int lineNumber) {
    node *nd = malloc(sizeof(node));
    nd->type = type;
    nd->value = value;
    
    nd->chld =  malloc(sizeof(node *)*4);
    nd->chld[0] = s1;
    nd->chld[1] = s2;
    nd->chld[2] = s3;
    nd->chld[3] = s4;

    nd->lineNumber = lineNumber;

    return nd;
}

void printTree(node *root) {
    if(root == NULL) {
		return;
	}
	printNode(root, 1);
}

void printNode(node *node, int level) {
	if(node == NULL) {
		return;	
	}
	
	char* tabs = (char*) calloc(level, sizeof(char));
	int i;
	for(i=0; i<level; i++) {
		tabs[i] = '\t';	
	}
	
	switch(node->type) {
			/* OPERATIONS */
			case AST_OP_ADD:
				fprintf(stderr, "%s <ADD> \n", tabs);
				break;

			case AST_OP_SUB:
				fprintf(stderr, "%s <SUB> \n", tabs);
				break;
				
			case AST_OP_MUL:
				fprintf(stderr, "%s <MUL> \n", tabs);
				break;

			case AST_OP_DIV:
				fprintf(stderr, "%s <DIV> \n", tabs);
				break;

			case AST_OP_G:
				fprintf(stderr, "%s <G> \n", tabs);
				break;

			case AST_OP_L:
				fprintf(stderr, "%s <L> \n", tabs);
				break;

			case AST_OP_GE:
				fprintf(stderr, "%s <GE> \n", tabs);
				break;

			case AST_OP_LE:
				fprintf(stderr, "%s <LE> \n", tabs);
				break;

			case AST_OP_AND:
				fprintf(stderr, "%s <AND> \n", tabs);
				break;

			case AST_OP_OR:
				fprintf(stderr, "%s <OR> \n", tabs);
				break;

			case AST_OP_REF:
				fprintf(stderr, "%s <REF> \n", tabs);
				break;

			case AST_OP_DEREF:
				fprintf(stderr, "%s <DEREF> \n", tabs);
				break;

			case AST_OP_EQ:
				fprintf(stderr, "%s <EQ> \n", tabs);
				break;

			case AST_OP_NE:
				fprintf(stderr, "%s <NE> \n", tabs);
				break;

			/* COMMANDS */
			case AST_CMD_ATTR:
				fprintf(stderr, "%s <ATTR> \n", tabs);
				break;

			case AST_CMD_INPUT:
				fprintf(stderr, "%s <INPUT> \n", tabs);
				break;

			case AST_CMD_RETURN:
				fprintf(stderr, "%s <RETURN> \n", tabs);
				break;

			case AST_CMD_OUTPUT:
				fprintf(stderr, "%s <OUTPUT> \n", tabs);
				break;

			case AST_CMD_IF:
				fprintf(stderr, "%s <IF> \n", tabs);
				break;

			case AST_CMD_LOOP:
				fprintf(stderr, "%s <LOOP> \n", tabs);
				break;

			case AST_CMD_FUNCTION_CALL:
				fprintf(stderr, "%s cmd<FUNCTION_CALL> \n", tabs);
				break;	

			case AST_EXPR_FUNCTION_CALL:
				fprintf(stderr, "%s expr<FUNCTION_CALL> \n", tabs);
				break;

			case AST_CMD_BLOCK:
				fprintf(stderr, "%s <BLOCK> \n", tabs);
				break;

			/* DEFINITIONS */
			case AST_DEF_FUNC:
				fprintf(stderr, "%s <DEF_FUNC> \n", tabs);
				break;
			case AST_DEF_UBIQ_VAR:
				fprintf(stderr, "%s <DEF_UBIQ_VAR> \n", tabs);
				break;

			case AST_DEF_GLOBAL_VAR:
				fprintf(stderr, "%s <DEF_GLOBAL_VAR> \n", tabs);
				break;

			case AST_LST_GBL_DEF:
				fprintf(stderr, "%s <GBL_DEF> \n", tabs);
				break;

			/* LISTAS */
			case AST_LST_PARAM_FUNC_CALL:
				fprintf(stderr, "%s <LST_PARAM_FUNC_CALL> \n", tabs);
				break;

			case AST_LST_LOCAL_VAR:
				fprintf(stderr, "%s <LST_LOCAL_VAR> \n", tabs);
				break;

			case AST_LST_INIT_VEC:
				fprintf(stderr, "%s <LST_INIT_VEC> \n", tabs);
				break;

			case AST_LST_CMD:
				fprintf(stderr, "%s <LST_CMD> \n", tabs);
				break;

			case AST_LST_PRINT_VAL:
				fprintf(stderr, "%s <LST_PRINT_VAL> \n", tabs);
				break;

			case AST_LST_PARAM_FUNC:
				fprintf(stderr, "%s <LST_PARAM_FUNC> \n", tabs);
				break;

			/* TERMINALS */
			case AST_LITERAL:
				fprintf(stderr, "%s <LITERAL> %s \n", tabs, node->value->value);
				break;

			case AST_IDENTIFIER:
				fprintf(stderr, "%s <IDENTIFIER> %s \n", tabs, node->value->value);
				break;

			/* RETURN TYPES */
			case AST_ID_VAL:
				fprintf(stderr, "%s <ID_VAL> \n", tabs);
				break;

			case TYPE_BOOL:
				fprintf(stderr, "%s <TYPE_BOOL> \n", tabs);
				break;

			case TYPE_WORD:
				fprintf(stderr, "%s <TYPE_WORD> \n", tabs);
				break;

			case TYPE_BYTE:
				fprintf(stderr, "%s <TYPE_BYTE> \n", tabs);
				break;

			default:
				fprintf(stderr, "\n Damn nigga... %d \n", node->type);	
	}

	free(tabs);
	for(i=0; i<4; i++) {
		printNode(node->chld[i], level+1);	
	}	
}


void decompileProgram(node* root, node* father, FILE* fp) {

	if(root == NULL) {
		return;
	}

	switch(root->type) {
		/* OPERATIONS */
			case AST_OP_ADD:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " + ");
				decompileProgram(root->chld[1], root, fp);				
				break;

			case AST_OP_SUB:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " - ");
				decompileProgram(root->chld[1], root, fp);
				break;
				
			case AST_OP_MUL:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " * ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_DIV:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " / ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_G:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " > ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_L:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " < ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_GE:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " >= ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_LE:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " <= ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_AND:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " && ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_OR:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " || ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_REF:
				fprintf(fp, " &");
				decompileProgram(root->chld[0], root, fp);
				break;

			case AST_OP_DEREF:
				fprintf(fp, " *");
				decompileProgram(root->chld[0], root, fp);
				break;

			case AST_OP_EQ:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " == ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_OP_NE:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " != ");
				decompileProgram(root->chld[1], root, fp);
				break;

			/* COMMANDS */
			case AST_CMD_ATTR:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " = ");
				decompileProgram(root->chld[1], root, fp);
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_INPUT:
				fprintf(fp, " input ");
				decompileProgram(root->chld[0], root, fp);
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_RETURN:
				fprintf(fp, " return ");
				decompileProgram(root->chld[0], root, fp);
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_OUTPUT:
				fprintf(fp, " output ");
				decompileProgram(root->chld[0], root, fp);
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_IF:
				fprintf(fp, " if ( ");
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " ) then ");
				decompileProgram(root->chld[1], root, fp);
				if (root->chld[2] != NULL) {
					fprintf(fp, " else ");
					decompileProgram(root->chld[2], root, fp);
				}
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_LOOP:
				fprintf(fp," loop (");
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp,")\n");
				decompileProgram(root->chld[1], root, fp);
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_FUNCTION_CALL:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, "(");
				if(root->chld[1] != NULL) {
					decompileProgram(root->chld[1], root, fp);				
				}
				fprintf(fp,")");
				if(father->type == AST_LST_CMD)
				    fprintf(fp, ";\n");
				break;

			case AST_CMD_BLOCK:
				fprintf(fp," {\n");
				decompileProgram(root->chld[0], root, fp); 
				fprintf(fp," }");
				break;

			/* DEFINITIONS */
			case AST_DEF_FUNC:
				fprintf(fp, "\n");
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " (");
				if (root->chld[1] != NULL) {
					decompileProgram(root->chld[1], root, fp);
				}
				fprintf(fp, ")\n");
				if (root->chld[2] != NULL) {
					decompileProgram(root->chld[2], root, fp);
				}
				fprintf(fp, "{\n");
				decompileProgram(root->chld[3], root, fp);
				fprintf(fp, "}\n");
				break;

			case AST_DEF_UBIQ_VAR:
				decompileProgram(root->chld[0], root, fp);
				if (root->chld[1] != NULL) {
					fprintf(fp, " $");
					decompileProgram(root->chld[1], root, fp);
					
				}
				fprintf(fp, ":");
				decompileProgram(root->chld[2], root, fp);
				fprintf(fp, ";\n");
				break;

			case AST_DEF_GLOBAL_VAR:
				decompileProgram(root->chld[0], root, fp);
				if (root->chld[1] != NULL) {
					fprintf(fp, "$");
					decompileProgram(root->chld[1], root, fp);
					fprintf(fp, ":");
					decompileProgram(root->chld[2], root, fp);
				}
				else {
					if (root->chld[2] != NULL) {
						if (root->chld[3] != NULL) {
							fprintf(fp, "[");
							decompileProgram(root->chld[3], root, fp);
							fprintf(fp, "]:");
							decompileProgram(root->chld[2], root, fp);
						}
						else {
							fprintf(fp, ":");
							decompileProgram(root->chld[2], root, fp);
						}
					}
					else {
						fprintf(fp, "[");
						decompileProgram(root->chld[3], root, fp);
						fprintf(fp, "]");
					}
				}
				fprintf(fp, ";\n");
				break;

			case AST_LST_GBL_DEF:
				decompileProgram(root->chld[0], root, fp);
				decompileProgram(root->chld[1], root, fp);
				break;

			/* LISTAS */
			case AST_LST_PARAM_FUNC_CALL:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, ",");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_LST_LOCAL_VAR:
				decompileProgram(root->chld[0], root, fp);
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_LST_INIT_VEC:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_LST_CMD:
				decompileProgram(root->chld[0], root, fp);
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_LST_PRINT_VAL:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, " , ");
				decompileProgram(root->chld[1], root, fp);
				break;

			case AST_LST_PARAM_FUNC:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, ", ");
				decompileProgram(root->chld[1], root, fp);
				break;

			/* TERMINALS */
			case AST_LITERAL:
				fprintf(fp, "%s", root->value->value);
				break;

			case AST_IDENTIFIER:
			    if(root->value != NULL) {
				    fprintf(fp, "%s", root->value->value);
		        }
		        else {
		            decompileProgram(root->chld[0], root, fp);
		            fprintf(fp, "[");
		            decompileProgram(root->chld[1], root, fp);
		            fprintf(fp, "]");
		        }
				break;

			/* RETURN TYPES */
			case AST_ID_VAL:
				decompileProgram(root->chld[0], root, fp);
				decompileProgram(root->chld[1], root, fp);
				break;

			case TYPE_BOOL:
				fprintf(fp, "bool ");
				break;

			case TYPE_WORD:
				fprintf(fp, "word ");
				break;

			case TYPE_BYTE:
				fprintf(fp, "byte ");
				break;

			/* MISC */
			case AST_PAREN:
				fprintf(fp, "(");
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, ")");
				break;

			case AST_EXPR_FUNCTION_CALL:
				decompileProgram(root->chld[0], root, fp);
				fprintf(fp, "(");
				if(root->chld[1] != NULL) {
					decompileProgram(root->chld[1], root, fp);				
				}
				fprintf(fp,")");
				break;

			default:
				fprintf(fp, "\n Damn nigga... %d \n", root->type);
	}
}

extern table symbolsTable;

// Checks if this variable was already declared in this scope.
// It returns:
//      NULL if not declared.
//      row of declared.
row *isDeclaredOn(char *id, unsigned int scope) {
    row* r = get(id, &symbolsTable);
    while(r != NULL) {
        if( (r->scope == scope) && (strcmp(id,r->value) == 0) ) {
            return r;
        }
        r = r->next;
    }
    return NULL;
}


void declareOn(int scope, int localI, int nature, int dataType, node *id) {
    // Find first entry not declared in hash. 
    row* r = get(id->value->value, &symbolsTable);
    while(r->scope != -1) {
        r = r->next;
    }
    // Declare.
    r->scope = scope;
    r->nature = nature;
    r->dataType = dataType;
    r->localI = localI;
    // For faster access.
    id->value = r;
}

void handleVarDef(node *def, int scope, int localN) {
    node *varDec;
    node *varId;
    int nature;
    int dataType;

    // Find info.
    if( def->chld[0]->type == AST_ID_VAL) {
        varDec = def->chld[0];
        varId = varDec->chld[1];
        
        if(def->chld[3] == NULL) {
            nature = NATURE_SCALAR;
        }
        else {
            nature = NATURE_VEC;
        }
        
        dataType = varDec->chld[0]->type;
        
    }
    else {
        varId = def->chld[1];
        nature = NATURE_PTR;
        dataType = def->chld[0]->type;
    }
    row *decl = isDeclaredOn(varId->value->value,scope);
    if( (decl != NULL) && (decl->scope == scope) ) {
		fprintf(stderr,"\n [LINE %d]", varId->lineNumber);
        fprintf(stderr,"\n %s REDECLARED!!!\n",varId->value->value);
    }
    else {
        fprintf(stderr,"\n[LINE %d] declare %s on %d\n",varId->lineNumber, varId->value->value,scope);
        declareOn(scope,localN,nature,dataType,varId);
    }
}

void checkFunctionUndefined(node *root, int scope) {
    if(root == NULL) {
        return;
    }
    int i = 0;
    row *dec;
    switch(root->type) {
        case AST_IDENTIFIER:
             // Beware of vectors.
             if(root->value != NULL) {
                // Find most specific declaration.
                dec = isDeclaredOn(root->value->value,scope);
                if(dec == NULL) {
                    dec = isDeclaredOn(root->value->value,0);
                }
                if( dec != NULL ) {
                    // Keep track of info.
                    root->value->scope = dec->scope;
                    root->value->nature = dec->nature;
                    root->value->dataType = dec->dataType;
                    root->value->args = dec->args;
                    root->value->localI = dec->localI;
                    fprintf(stderr,"used %s on %d\n",root->value->value,scope);
                }
                else {
					fprintf(stderr,"\n [LINE %d]", root->lineNumber);
                    fprintf(stderr,"%s UNDEFINED on %d and 0\n",root->value->value,scope);
                }
            }
            break;
    }
    
    for(;i<4; i++) {
		checkFunctionUndefined(root->chld[i],scope);
	}
}

void checkUndefined(node *root, int scope) {
    if(root == NULL) {
        return;
    }
    int i = 0;
    switch(root->type) {
        case AST_LST_GBL_DEF:
		    if( root->chld[1]->type == AST_DEF_FUNC) {
		        checkFunctionUndefined(root->chld[1],scope);
		        scope++;
		    }
		    checkUndefined(root->chld[0],scope);
            break;
    }
    
    for(;i<4; i++) {
		checkUndefined(root->chld[i],scope);
	}
}

void checkDefines(node *root, int scope, int localN) {
    if(root == NULL) {
        return;
    }
    
    // Auxiliar vars for holding info.
    node *idVal, *list, *id, *type;
    param *args;
    
    switch(root->type) {
        case AST_LST_GBL_DEF:
		    checkDefines(root->chld[1],scope,localN);
		    if(root->chld[1]->type == AST_DEF_FUNC) {
		        scope++;
		    }
		    checkDefines(root->chld[0],scope,localN);
            break;
        case AST_LST_LOCAL_VAR:
            checkDefines(root->chld[1],scope,localN+1);
            checkDefines(root->chld[0],scope,localN);
            break;
        case AST_DEF_GLOBAL_VAR:
            handleVarDef(root,0,-1);
            break;
        case AST_DEF_UBIQ_VAR:
            handleVarDef(root,scope,localN);
            break;
        case AST_DEF_FUNC:
            localN = 0;
            
            // Define function.
            idVal = root->chld[0];
            declareOn(0,-1,NATURE_FUNC, idVal->chld[0]->type, idVal->chld[1]);
            
            // Initialize parameter list.
            idVal->chld[1]->value->args = (param *) malloc(sizeof(param));
            args = idVal->chld[1]->value->args;
            
            // Go over parameters.
            list = root->chld[1];
            while(list != NULL) {
                if(list->type == AST_LST_PARAM_FUNC) {
                    idVal = list->chld[1];  
                    list = list->chld[0];
                }
                else {
                    idVal = list;
                    list = NULL;
                }
                
                // Declare.
                type = idVal->chld[0];
                id = idVal->chld[1];
                declareOn(scope,localN, NATURE_SCALAR, type->type, id);
                
                // Move stack one.
                localN += 1;
                
                // Add parameter to list.
                args->v = id->value;
                args->next = (param *) malloc(sizeof(param));
                args = args->next;
            }
            
            // Go over local vars.
            checkDefines(root->chld[2],scope,localN);
            break;
    }
}

void checkNature(node *root) {
    if(root == NULL) {
        return;
    }
    int i = 0;
    int op;
    
    switch(root->type) {
        /* Ignore Definitions */
        case AST_DEF_GLOBAL_VAR:
            i = 4;
            break;
        case AST_DEF_UBIQ_VAR:
            i = 4;
            break;
        case AST_DEF_FUNC:
            i = 3;
            break;
        
        /* Check Nature */
        case AST_CMD_FUNCTION_CALL:
            if(root->chld[0]->value->nature != NATURE_FUNC) {
				fprintf(stderr,"\n [LINE %d]", root->chld[0]->lineNumber);
                fprintf(stderr,"%s should be func\n",root->chld[0]->value->value);
            }
            i = 1;
            break;
        case AST_EXPR_FUNCTION_CALL:
            if(root->chld[0]->value->nature != NATURE_FUNC) {
				fprintf(stderr,"\n [LINE %d]", root->chld[0]->lineNumber);
                fprintf(stderr,"%s should be func\n",root->chld[0]->value->value);
            }
            i = 1;
            break;
        case AST_IDENTIFIER:
            if( (root->chld[1] != NULL) 
                    && (root->chld[1]->value != NULL)
                    
                    && (root->chld[1]->value->type != SYMBOL_LIT_INTEGER)
                    && (root->chld[1]->value->type != SYMBOL_LIT_CHAR)
                    
                    && (root->chld[1]->value->nature != NATURE_SCALAR) ) {
                fprintf(stderr,"\n [LINE %d]", root->chld[1]->lineNumber);
				fprintf(stderr,"vec index must be scalar.\n");
				fprintf(stderr," type:%d\n",root->chld[1]->value->type);
            }
            if( (root->chld[1] != NULL) && (root->chld[0]->value->nature != NATURE_VEC) ) {
				   fprintf(stderr,"\n [LINE %d]", root->chld[0]->lineNumber);
                   fprintf(stderr,"should be vec\n");
            }
            else if( (root->value != NULL) 
                    && (root->value->nature != NATURE_SCALAR) 
                    && (root->value->nature != NATURE_PTR) ){
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
                fprintf(stderr,"%s should be scalar\n",root->value->value); 
            }
            i = 2;
    }

    for(;i<4; i++) {
		checkNature(root->chld[i]);
	}
}

// Pointers are their type, but negative.
#define isPtr(op) (op < 0)

int checkAritm(int op1, int op2, int lineNumber) {
    // Check boolean.
    if( (op1 == TYPE_BOOL) || (op2 == TYPE_BOOL) ) {
		fprintf(stderr,"\n [LINE %d]", lineNumber);        
		fprintf(stderr,"aritm has bool\n");      
    }
    // Use best dataType.
	if( (op1 == TYPE_WORD) || (op2 == TYPE_WORD) ) {
	    return TYPE_WORD;
	}
	else {
	    return TYPE_BYTE;
	}
}

int checkComparison(int op1, int op2, int lineNumber) {
    // Comparing two numbers.
	if( (op1 != TYPE_BOOL) && (op2 != TYPE_BOOL) && !isPtr(op1) && !isPtr(op2) ) {
	    return TYPE_BOOL;
	}
	fprintf(stderr,"\n [LINE %d]", lineNumber);
	fprintf(stderr,"Comparison not valid\n");
}

int checkBoolOp(int op1, int op2, int lineNumber) {
    if( (op1 == TYPE_BOOL) && (op2 == TYPE_BOOL) ){
        return TYPE_BOOL;
    }
    fprintf(stderr,"\n [LINE %d]", lineNumber);        
    fprintf(stderr,"Boolean op not valid\n");
}

int checkCompatible(int op1, int op2) {
    if( (op1 == op2) || (isPtr(op1) && isPtr(op2)) ) {
        return 1;
    }
    else {
        if( ( (op1 == TYPE_BYTE) || (op1 == TYPE_WORD) ) 
            && ( (op2 == TYPE_BYTE) || (op2 == TYPE_WORD) )  ) {
            return 1;
        }  
    }
    return 0;
}

int checkTypes(node *root, node *father) {
    if(root == NULL) {
        return;
    }
    int i = 0;
    // Auxiliar variables used inside switch.
    int op1, op2;
    node *list;
    
    switch(root->type) {
    	/* OPERATIONS */
    	case AST_EXPR_FUNCTION_CALL:
	        op1 = checkTypes(root->chld[0], root);
            return op1;
	        break;
    	
		case AST_OP_ADD:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);

		    // Check ptr.
            if( isPtr(op1) && isPtr(op2) ) {
                fprintf(stderr,"\n [LINE %d]", root->lineNumber);
                fprintf(stderr,"sum has two ptr\n");
            }
            else {
                if(isPtr(op1)) {
                    return op1;
                }
                else if(isPtr(op2)) {
                    return op2;
                }
            }
            
		    return checkAritm(op1,op2, root->lineNumber);
			break;
			
		case AST_OP_SUB:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    if( isPtr(op1) || isPtr(op2) ) {
                fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"ptr not allowed on aritm\n");
		    }
		    return checkAritm(op1,op2, root->lineNumber);
			break;
			
		case AST_OP_MUL:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    if( isPtr(op1) || isPtr(op2) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"ptr not allowed on aritm\n");
		    }
		    return checkAritm(op1,op2, root->lineNumber);
			break;
			
		case AST_OP_DIV:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    if( isPtr(op1) || isPtr(op2) ) {
		        fprintf(stderr,"\n [LINE %d]", root->lineNumber);
				fprintf(stderr,"ptr not allowed on aritm\n");
		    }
		    return checkAritm(op1,op2, root->lineNumber);
			break;
		
		/* Comparison */
		case AST_OP_G:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber); 
			break;
			
		case AST_OP_L:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber);
		    i = 2;
			break;
			
		case AST_OP_GE:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber);
			break;
			
		case AST_OP_LE:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber);
			break;
			
		case '>':
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber);
		    break;
		    
		case '<':
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkComparison(op1,op2, root->lineNumber);
		    break;
		
		/* Boolean Operations */
		case AST_OP_AND:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkBoolOp(op1,op2, root->lineNumber);
			break;
			
		case AST_OP_OR:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    
		    return checkBoolOp(op1,op2, root->lineNumber);
			break;
	    
	    /* Pointer Operations */
		case AST_OP_REF:
		    op1 = checkTypes(root->chld[0],root);
		    if( isPtr(op1) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"ref cannot be on ptr\n");
		    }
		    return -op1;
			break;
			
		case AST_OP_DEREF:
		    op1 = checkTypes(root->chld[0],root);
		    if( !isPtr(op1) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"deref must be on ptr\n");
		    }
		    return -op1;
			break;
	    
	    /* Equal Operations */
		case AST_OP_EQ:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    if( isPtr(op1) || isPtr(op2) || !checkCompatible(op1,op2) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"wrong eq\n");
		    }
		    return TYPE_BOOL;
			break;
			
		case AST_OP_NE:
		    op1 = checkTypes(root->chld[0],root);
		    op2 = checkTypes(root->chld[1],root);
		    if( isPtr(op1) || isPtr(op2) || !checkCompatible(op1,op2) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		        fprintf(stderr,"wrong neq\n");
		    }
		    return TYPE_BOOL;
			break;
	    
	    /* Commands */
	    case AST_CMD_ATTR:
	        op1 = checkTypes(root->chld[0],root);
	        op2 = checkTypes(root->chld[1],root);
	        if( !checkCompatible(op1,op2) ) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
	            fprintf(stderr,"incompatible values for attr\n");
	        }
	        return op1;
	        break;
	    
	    case AST_CMD_INPUT:
				op1 = checkTypes(root->chld[0],root);
				if( isPtr(op1) ) {
					fprintf(stderr,"\n [LINE %d]", root->lineNumber);
				    fprintf(stderr,"reading ptr?\n");
				}
				i = 4;
				break;
	    
	    case AST_CMD_RETURN:
				// Go over this one, since it is not checked here.
				i = 4;
				break;
	    
	    case AST_CMD_OUTPUT:
				list = root->chld[0];
			    while(list != NULL) {
			        if(list->type == AST_LST_PRINT_VAL) {
			            op1 = checkTypes(list->chld[1],root);
			            list = list->chld[0];
			        }
			        else {
			            op1 = checkTypes(list,root);
			            list = NULL;
			        }
			        
			        if( isPtr(op1) 
			            || ( (op1 != TYPE_BOOL) && (op1 != TYPE_WORD) 
			                && (op1 != TYPE_BYTE) && (op1 != SYMBOL_LIT_STRING) )
			                 ) {
						fprintf(stderr,"\n [LINE %d]", root->lineNumber);			            
						fprintf(stderr,"invalid output parameter %d\n",op1);
			        }
			    }
				break;
	    
	    case AST_CMD_IF:
	        op1 = checkTypes(root->chld[0],root);
	        if( op1 != TYPE_BOOL ) {
	            fprintf(stderr,"\n [LINE %d]", root->lineNumber);
				fprintf(stderr,"if condition not bool\n");
	        }
	        i = 1;
	        break;
	    
	    case AST_CMD_LOOP:
	        op1 = checkTypes(root->chld[0],root);
	        if( op1 != TYPE_BOOL ) {
	            fprintf(stderr,"\n [LINE %d]", root->lineNumber);
				fprintf(stderr,"loop condition not bool\n");
	        }
	        i = 1;
	        break;
	    
	    case AST_CMD_FUNCTION_CALL:
                //op1 = checkTypes(root->chld[0], root);
                //fprintf(stderr,"FUN type %d",op1);
                //return op1;
                // ???
				break;
	    
	    /* Ignore Defines */
	    case AST_DEF_FUNC:
	            // Go to  block.
				i = 2;
				break;

		case AST_DEF_UBIQ_VAR:
		
			op1 = checkTypes(root->chld[0], root);
			op2 = checkTypes(root->chld[2], root);
			
			// Case Pointer.
			if(root->chld[1] != NULL) {
			    if( (op2 != TYPE_WORD) && (op2 != TYPE_BYTE) ) {
				   fprintf(stderr,"\n [LINE %d]", root->lineNumber);
			       fprintf(stderr,"pointer initialization invalid\n");
			    }
			}
			else if( !checkCompatible(op1,op2) ) {
					fprintf(stderr,"\n [LINE %d]", root->lineNumber);	               
					fprintf(stderr,"incompatible init value for scalar var %d %d \n",op1,op2);
	           }
	           i = 4;
			break;

		case AST_DEF_GLOBAL_VAR:
			if (root->chld[1] != NULL) {
			    // Pointer.
			    op2 = checkTypes(root->chld[2], root);
				if( (op2 != TYPE_WORD) && (op2 != TYPE_BYTE) ) {
     			   fprintf(stderr,"\n [LINE %d]", root->lineNumber);
			       fprintf(stderr,"pointer initialization invalid\n");
			    }
			}
			else {
				if (root->chld[2] != NULL) {
					if (root->chld[3] != NULL) {
			    		// Vector initialized.
			    		op2 = checkTypes(root->chld[3], root);
					    if( (op2 != TYPE_WORD) && (op2 != TYPE_BYTE) ) {
					        fprintf(stderr,"\n [LINE %d]", root->lineNumber);
							fprintf(stderr,"vector size must be int\n");
					    }
					    
					    list = root->chld[2];
					    op1 =  checkTypes(root->chld[0], root);
					    while(list != NULL) {
					        if( list->type == AST_LST_INIT_VEC ) {
					            op2 = checkTypes(list->chld[1], root);
					            list = list->chld[0];
					        }
					        else {
					            op2 = checkTypes(list, root);
					            list = NULL;
					        }
					        
					        if( !checkCompatible(op1,op2) ) {
								fprintf(stderr,"\n [LINE %d]", root->lineNumber);                            
								fprintf(stderr,"incompatible values for attr\n");
	                        }
					    }
					}
					else {
					    // Scalar initialized.
					    op1 = checkTypes(root->chld[0], root);
					    op2 = checkTypes(root->chld[2], root);
					    if( !checkCompatible(op1,op2) ) {
	                        fprintf(stderr,"\n [LINE %d]", root->lineNumber);
							fprintf(stderr,"incompatible init value for scalar var\n");
	                    }
					}
				}
				else {
					// Vector not initialized.
					op2 = checkTypes(root->chld[3], root);
					if( (op2 != TYPE_WORD) && (op2 != TYPE_BYTE) ) {
						fprintf(stderr,"\n [LINE %d]", root->lineNumber);
					    fprintf(stderr,"vector size must be int\n");
					}
				}
		    }
			i = 4;
		    break;

		/* Lists */
		case AST_LST_PARAM_FUNC_CALL:
		    // This is checked later.
			i = 4;
			break;
	    
	    case AST_LST_PRINT_VAL:
	        op1 = checkTypes(root->chld[0],root);
	        return op1;
	        break;
	    
	    /* Leaf Nodes */
	    case AST_PAREN:
	        op1 = checkTypes(root->chld[0],root);
	        return op1;
	        break;
	    
	    case AST_ID_VAL:
	        op1 = checkTypes(root->chld[0],root);
	        return op1;
	        break;
	    
	    case AST_IDENTIFIER:
	    
		    if(root->value != NULL) {
		        if(root->value->nature == NATURE_PTR) {
		            return -1*root->value->dataType;
		        }
		    }
		    else {
		        op2 = checkTypes(root->chld[1],root);
		        if( op2 == TYPE_BOOL ) {
		            fprintf(stderr,"\n [LINE %d]", root->lineNumber);
					fprintf(stderr,"vec index bool\n");
		        }
		        else if( isPtr(op2) ) {
					fprintf(stderr,"\n [LINE %d]", root->lineNumber);
		            fprintf(stderr,"vec index ptr\n");
		        }
		        
		        return checkTypes(root->chld[0],root);
		    }
		    return root->value->dataType;
			break;
	    
	    case AST_LITERAL:
	        if(root->value->type == SYMBOL_LIT_INTEGER) {
	            return TYPE_WORD;
	        }
	        if( (root->value->type == SYMBOL_LIT_TRUE)
	            || (root->value->type == SYMBOL_LIT_FALSE)  ) {
	            return TYPE_BOOL;
	        }
	        if(root->value->type == SYMBOL_LIT_CHAR) {
	            return TYPE_BYTE;
	        }
	        if(root->value->type == SYMBOL_LIT_STRING) {
	            return SYMBOL_LIT_STRING;
	        }
			fprintf(stderr,"\n [LINE %d]", root->lineNumber);
			fprintf(stderr,"literal TYPE %d not allowed\n",root->value->type);
			break;
			
		case TYPE_BOOL:
		    return TYPE_BOOL;
			break;

		case TYPE_WORD:
		    return TYPE_WORD;
		    break;

		case TYPE_BYTE:
			return TYPE_BYTE;
			break;
    }
    
    for(;i<4; i++) {
		checkTypes(root->chld[i],root);
	}
}

int checkFunctionReturnType(node *root) {
     if(root == NULL) {
        return;
    }
    int i = 0;
    int fdt,rdt;
    node *list;

    if(root->type == AST_DEF_FUNC) {
            fdt = checkTypes(root->chld[0],root);
            
            // Search for return.
            list = root->chld[3];
            while( (list != NULL) && (list->type == AST_LST_CMD) ) { 
                if(list->chld[0] != NULL) {
                    if(list->chld[0]->type == AST_CMD_RETURN) {
                        list = list->chld[0];
                        break;
                    }
                }    
                 if(list->chld[1] != NULL) {
                    if(list->chld[1]->type == AST_CMD_RETURN) {
                        list = list->chld[1];
                        break;
                    }
                }
                list = list->chld[0];
            }
            
            // Compare types.
            if(list == NULL) {
				fprintf(stderr,"\n [LINE %d]", root->lineNumber);
                fprintf(stderr,"return cmd missing\n");
            }
            else {
                rdt = checkTypes(list->chld[0],root);
                if(!checkCompatible(fdt,rdt)) {
					fprintf(stderr,"\n [LINE %d]", list->chld[0]->lineNumber);
                    fprintf(stderr,"%d %d ",fdt,rdt);
                    fprintf(stderr,"incompatible return type\n");
                }
            }
            i = 4;
    }
    
    for(;i<4; i++) {
		checkFunctionReturnType(root->chld[i]);
	}
}

void checkFunctionParameters(node *root) {
    if(root == NULL) {
        return;
    }
    int i = 0;
    int typeCall, typeDef;
    // TODO: check nature.
    if( (root->type == AST_CMD_FUNCTION_CALL) 
            || (root->type == AST_EXPR_FUNCTION_CALL) ) {
            
        param *defArgs = root->chld[0]->value->args;
        node *lstCall =  root->chld[1];
        
        // TODO: Refactor list iteration.
        while( (lstCall != NULL) && (lstCall->type == AST_LST_PARAM_FUNC_CALL) ) {
            if(lstCall->chld[1] != NULL) {
                    if(lstCall->chld[1]->type != AST_LST_PARAM_FUNC_CALL) {
                        typeCall = checkTypes(lstCall->chld[1],root);
                        if( (defArgs == NULL) || (defArgs->v == NULL) ) {
                            fprintf(stderr,"\n [LINE %d]", lstCall->chld[1]->lineNumber);
							fprintf(stderr,"too many params.\n");
                        }
                        else {
                            typeDef = defArgs->v->dataType;
                            if( !checkCompatible(typeDef,typeCall) ) {
								fprintf(stderr,"\n [LINE %d]", lstCall->chld[1]->lineNumber);
                                fprintf(stderr,"incopatible param type.\n");
                            }
                            defArgs = defArgs->next;
                        }
                    }
                }    
            if(lstCall->chld[0] != NULL) {
                if(lstCall->chld[0]->type != AST_LST_PARAM_FUNC_CALL) {
                    typeCall = checkTypes(lstCall->chld[0],root);
                    if( (defArgs == NULL) || (defArgs->v == NULL) ) {
						fprintf(stderr,"\n [LINE %d]", lstCall->chld[0]->lineNumber);
                        fprintf(stderr,"too many params.\n");
                    }
                    else {
                        typeDef = defArgs->v->dataType;
                        if( !checkCompatible(typeDef,typeCall) ) {
							fprintf(stderr,"\n [LINE %d]", lstCall->chld[0]->lineNumber);
                            fprintf(stderr,"incopatible param type.\n");
                        }
                        defArgs = defArgs->next;
                    } 
                }
            }
            lstCall = lstCall->chld[0];
        }
        if( (defArgs != NULL) && (defArgs->v != NULL) ) {
			fprintf(stderr,"\n [LINE %d]", root->lineNumber);
            fprintf(stderr,"too few param.\n");
        }
        
        i = 4;
    }
    
    
    for(;i<4; i++) {
		checkFunctionParameters(root->chld[i]);
	}
}
